package cn.ayanami.blog.article.service.impl;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import cn.ayanami.blog.article.entity.dao.ArticleCategoryDao;
import cn.ayanami.blog.article.entity.dao.ArticleDao;
import cn.ayanami.blog.article.entity.dao.CategoryDao;
import cn.ayanami.blog.article.entity.dto.AddArticleDto;
import cn.ayanami.blog.article.entity.dto.ModifyArticleDto;
import cn.ayanami.blog.article.entity.po.*;
import cn.ayanami.blog.article.entity.vo.*;
import cn.ayanami.blog.article.service.ArticleService;
import cn.hutool.core.lang.UUID;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Service
public class ArticleServiceImpl implements ArticleService {

    @Autowired
    private ArticleDao articleDao;

    @Autowired
    private ArticleCategoryDao articleCategoryDao;

    @Autowired
    private CategoryDao categoryDao;

    @Transactional
    @Override
    public Mono<Integer> addArticle(AddArticleDto addArticleDto) {
        var articlePo = new ArticlePo();
        BeanUtils.copyProperties(addArticleDto, articlePo);
        String articleId = UUID.fastUUID().toString(true);
        articlePo.setArticleId(articleId);
        articlePo.setPublishTime(LocalDateTime.now());
        if (addArticleDto.getArticleText() == null || addArticleDto.getArticleText().length() <= 45) {
            articlePo.setArticleBrief(addArticleDto.getArticleTitle());
        } else {
            articlePo.setArticleBrief(addArticleDto.getArticleText().substring(0, 45));
        }

        // 可设置多分类
        if (addArticleDto.getCategoryList() != null && !addArticleDto.getCategoryList().isEmpty()) {
            var categories = categoryDao.findAllById(addArticleDto.getCategoryList());

            categories.map(category -> {
                var articleCategoryPo = new ArticleCategoryPo();
                articleCategoryPo.setArticleId(articleId);
                articleCategoryPo.setCategoryId(category.getId());

                return articleCategoryPo;
            }).collectList().subscribe(r -> {
                articleCategoryDao.saveAll(r).subscribe(e -> {
                }, Throwable::printStackTrace);
            });
        }
        articleDao.save(articlePo).subscribe();
        return Mono.just(Integer.valueOf(1));
    }

    @Override
    public Mono<Integer> modifyArticle(ModifyArticleDto modifyArticleDto) {
        var articlePo = articleDao.findByArticleId(modifyArticleDto.getAritcleId());

        return articlePo.mapNotNull(po -> {
            BeanUtils.copyProperties(modifyArticleDto, po);
            articleDao.save(po);
            return Integer.valueOf(1);
        }).defaultIfEmpty(-1);
    }

    @Override
    public Mono<ArticleVo> getArticleByArticleId(String id) {
        // Mono Flux 的作用就是构建流水线
        // 过程实际就是不断地使用Map或者FlatMap转为自己所需要数据形式，这里可以继续做截断，不要把回调写得太长
        var categoryVos = articleCategoryDao.findByArticleId(id).map(ArticleCategoryPo::getCategoryId).collectList()
                .flatMap(categoryIds -> categoryDao.findAllById(categoryIds).map(cpo -> {
                    var categoryVo = new CategoryVo();
                    BeanUtils.copyProperties(cpo, categoryVo);
                    return categoryVo;
                }).collectList());

        return categoryVos.flatMap(vos -> {
            return articleDao.findByArticleId(id).map(po -> {
                var vo = new ArticleVo();
                BeanUtils.copyProperties(po, vo);
                vo.setCategoryList(vos);
                return vo;
            });
        });

    }

    @Override
    public Flux<ArticleListVo> getArticleList(int page, int size) {
        // r2dbc好像还没有分页？
        // var poList = articleDao.findBy(PageRequest.of(page - 1, size, Sort.by(Direction.DESC, "publishTime")));
        List<Integer> types = new ArrayList<>(2);
        types.add(0);
        types.add(1);
        var poList = articleDao.findByPublishTypeIn(types, PageRequest.of(page - 1, size, Sort.by(Direction.DESC, "publishTime")));
        return poList.mapNotNull(po -> {
            var vo = new ArticleListVo();
            BeanUtils.copyProperties(po, vo);
            return vo;
        });
    }

}
